#include<iostream>
#include<string>
#include<cstring>
#include<cerrno>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<signal.h>
#include<pthread.h>//线程
// ./tcp_server 8081
void Usage(std::string proc)
{
    std::cout<<"Usage:"<<proc<<"port"<<std::endl;
}
void ServiceIO(int new_sock)
{
       while(true)
        {
            char buffer[1024];
            memset(buffer,0,sizeof(buffer));
            ssize_t s=read(new_sock,buffer,sizeof(buffer)-1);
            if(s>0)
            {
                buffer[s]=0;//将获取的内容当成字符串
                std::cout<<"client# "<<buffer<<std::endl;
                std::string echo_string=">>>server<<<, ";
                echo_string+=buffer;
                write(new_sock,echo_string.c_str(),echo_string.size());
            }
            else if(s==0)
            {
                std::cout<<"client quit ..."<<std::endl;
                break;
            }
            else
            {
                std::cerr<<"read error"<<std::endl;
                break;
            }
        }
}
void* HandlerRequest(void *args)
{
  pthread_detach(pthread_self());
  int sock=*((int*)args);
  delete (int*)args;
  ServiceIO(sock);
  close(sock);
}
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        return 1;
    }
    //1.创建套接字
    int listen_sock=socket(AF_INET,SOCK_STREAM,0);
    if(listen_sock<0)
    {
        std::cerr<<"socket error"<<errno<<std::endl;
    }
    //2.bind
    struct sockaddr_in local;
    memset(&local,0,sizeof(local));
    local.sin_family=AF_INET;
    local.sin_port=htons(atoi(argv[1]));
    local.sin_addr.s_addr=INADDR_ANY;
    if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local))<0)
    {
        std::cerr<<"bind error:"<<errno<<std::endl;
        return 3;
    }


    //3.因为tcp是面向连接的，a.在通信前，需要建连接 b.然后才能通信
    //一定有人主动建立（客户端，需要服务），一档有人被动接受连接（服务器，提供服务）
    //我们当前写的是一个server，周而复始的不间断的等待客户到来
    //我们要不断给用户提供一个建立连接的功能
    //设置套接字是listen状态，本质是允许用户连接
    const int back_log=5;
    if(listen(listen_sock,back_log)<0)
    {
        std::cerr<<"listen error"<<std::endl;
        return 4;
    }    
    //signal(SIGCHLD,SIG_IGN);//在Linux中父进程忽略子进程的SIGCHLD信号，子进程会自动退出释放资源
    for(;;)                 //就不需要父进程等待了
    {
        struct sockaddr_in peer;
        socklen_t len=sizeof(peer);
        int new_sock=accept(listen_sock,(struct sockaddr*)&peer,&len);//父进程来接受新连接，子进程来做服务
        if(new_sock<0)
        {
            continue;
        }
        uint16_t cli_port=ntohs(peer.sin_port );//网络转主机
        std::string cli_ip=inet_ntoa(peer.sin_addr);
        std::cout<<"get a new link ->:["<<cli_ip <<":"<<cli_port<<"]#"<<new_sock<<std::endl;
        //version3 线程
        // pthread_t tid;
        // int* pram=new int(new_sock);
        // pthread_create(&tid,nullptr,HandlerRequest,pram);
      

        //version2 多进程
        pid_t id=fork();
        if(id<0)
        {
            continue;
        }
        else if(id==0)//曾经经父进程打开的文件符，会被子进程继承
        {
            //child
            close(listen_sock);
            if(fork()>0) exit(0);
            ServiceIO(new_sock);
            close(new_sock);//如果不关闭文件描述符，会造成文件描述符泄露
            exit(0);
        }
        else
        {
            //father

            //donothing!
            waitpid(id,nullptr,0);
            close(new_sock);
        }
        //提供服务
        //version1:单进程版，没人使用
        //ServiceIO(new_sock);
    }

    return 0;
}









